import * as Cesium from 'cesium/Build/Cesium/Cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'

import * as THREE from 'three/build/three.module'

import { CesiumJsUtil } from './CesiumJsUtil/CesiumJsUtil'
import {ThreeJsUtil} from './ThreeJsUtil/ThreeJsUtil'

const cesiumApp = new CesiumJsUtil()
const threeApp = new ThreeJsUtil()

Cesium.buildModuleUrl.setBaseUrl('static/Cesium/')

export class CesiumThreeUtil {
  constructor () {
    this.cesium = cesiumApp
    this.three = threeApp
  }

  init (params, cesiumCallback, threeCallback) {
    const cesiumThreeParams = params['cesium_three']
    const cesiumParams = params.cesium
    const threeParams = params.three

    cesiumApp.init(cesiumParams, cesiumCallback)

    threeApp.init(threeParams, threeCallback)

    this.animate()
  }

  flyToObj(obj, option) {
    const that = this
    option = option || {}
    const vector = option.vector || new THREE.Vector3(-0.7, 0.7, 0.7)
    const radiusRatio = option.radiusRatio || 3
    const objCenter = threeApp.getObjWorldPosition(obj)
    const controls = [objCenter.x, objCenter.y, objCenter.z]
    var box = new THREE.Box3()
    const objRadius = box.expandByObject(obj).max.distanceTo(box.expandByObject(obj).min) / 2
    const distance = [vector.x * objRadius * radiusRatio, vector.y * objRadius * radiusRatio, vector.z * objRadius * radiusRatio]
    const position = [distance[0] + objCenter.x, distance[1] + objCenter.y, distance[2] + objCenter.z]

    // 飞行开始
    cesiumApp.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
    cesiumApp.viewer.camera.flyTo({
      destination: new Cesium.Cartesian3(...position),
      orientation: {
        heading: that.getHeading(new Cesium.Cartesian3(...position), new Cesium.Cartesian3(...controls)),
        pitch: that.getPitch(new Cesium.Cartesian3(...position), new Cesium.Cartesian3(...controls)),
        roll: 0.0,
      },
      duration: 3,
      complete: () => {
        // 到达位置后执行的回调函数
        cesiumApp.viewer.camera.lookAt(new Cesium.Cartesian3(...controls), new Cesium.HeadingPitchRange(that.getHeading(new Cesium.Cartesian3(...position), new Cesium.Cartesian3(...controls)), that.getPitch(new Cesium.Cartesian3(...position), new Cesium.Cartesian3(...controls)), new THREE.Vector3(...controls).distanceTo(new THREE.Vector3(...position))))
      },
      cancle:function () {
        // 如果取消飞行则会调用此函数
      }
    })
  }

  getHeading(position, controls) {
    //建立以点A为原点，X轴为east,Y轴为north,Z轴朝上的坐标系
    const transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
    //向量AB
    const positionvector = Cesium.Cartesian3.subtract(controls, position, new Cesium.Cartesian3());
    //因transform是将A为原点的eastNorthUp坐标系中的点转换到世界坐标系的矩阵
    //AB为世界坐标中的向量
    //因此将AB向量转换为A原点坐标系中的向量，需乘以transform的逆矩阵。
    const vector = Cesium.Matrix4.multiplyByPointAsVector(Cesium.Matrix4.inverse(transform, new Cesium.Matrix4()), positionvector, new Cesium.Cartesian3());
    //归一化
    const direction = Cesium.Cartesian3.normalize(vector, new Cesium.Cartesian3());
    //heading
    const heading = Math.atan2(direction.y, direction.x) - Cesium.Math.PI_OVER_TWO;
    return Cesium.Math.TWO_PI - Cesium.Math.zeroToTwoPi(heading)
  }

  getPitch(position, controls) {
    let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(position);
    const vector = Cesium.Cartesian3.subtract(controls, position, new Cesium.Cartesian3());
    let direction = Cesium.Matrix4.multiplyByPointAsVector(Cesium.Matrix4.inverse(transfrom, transfrom), vector, vector);
    Cesium.Cartesian3.normalize(direction, direction);
    //因为direction已归一化，斜边长度等于1，所以余弦函数等于direction.z
    return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
  }

  animate (callback) {
    const that = this
    threeApp.animate(() => {
      cesiumApp.viewer.render()

      that.animateThreeCamera()

      if (callback) callback()
    })
  }

  animateThreeCamera() {
    // register Three.js scene with Cesium

    threeApp.camera.fov = Cesium.Math.toDegrees(cesiumApp.viewer.camera.frustum.fovy) // ThreeJS FOV is vertical

    // three.camera.updateProjectionMatrix();

    // Configure Three.js meshes to stand against globe center position up direction

    threeApp.camera.matrixAutoUpdate = false

    var cvm = cesiumApp.viewer.camera.viewMatrix

    var civm = cesiumApp.viewer.camera.inverseViewMatrix

    threeApp.camera.matrixWorld.set(
      civm[0], civm[4], civm[8], civm[12],

      civm[1], civm[5], civm[9], civm[13],

      civm[2], civm[6], civm[10], civm[14],

      civm[3], civm[7], civm[11], civm[15]
    )

    threeApp.camera.matrixWorldInverse.set(
      cvm[0], cvm[4], cvm[8], cvm[12],

      cvm[1], cvm[5], cvm[9], cvm[13],

      cvm[2], cvm[6], cvm[10], cvm[14],

      cvm[3], cvm[7], cvm[11], cvm[15]
    )
    //
    threeApp.camera.updateProjectionMatrix()
  }
}
